// threadtest.cc 
//      Simple test case for the threads assignment.
//
//      Create two threads, and have them context switch
//      back and forth between themselves by calling Thread::Yield, 
//      to illustratethe inner workings of the thread system.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "synch.h"

//----------------------------------------------------------------------
// PassageAutreTest()
//      Permet de purger l'ordonnanceur de tous les threads des tests
//      précédents
//----------------------------------------------------------------------
void
PassageAutreTest(){
	for(int i=0;i<20;i++){  
		currentThread->Yield ();
	}
}

Lock *l1;      // define the mutex
Lock *l2;      // define the mutex
Condition *c1;  // define the condition
Communicator *com1; // define the communicator

//----------------------------------------------------------------------
// LockTest 
//             Teste les prises de verrous et les relaches
//----------------------------------------------------------------------

void
LockTest (int th1){
	printf("Le thread %d essaye de prendre le verrou\n",th1);
	l1->Acquire();
	printf("Le thread %d vient de prendre le verrou\n",th1);
	currentThread->Yield();
	printf("Le thread %d vient d'etre ordonnance\n",th1);
	l1->Release();
	printf("Le thread %d vient de relacher le verrou\n",th1);
}

//----------------------------------------------------------------------
// ConditionTest 
//             Effectue les tests de la classe Condition
//----------------------------------------------------------------------

void
ConditionTestWait (int which){
	
	l1->Acquire();
	printf ("*** Le thread %d va attendre\n", which);
	c1->Wait(l1);
	printf ("*** Le thread %d est revenu\n", which);
	l1->Release();
	
}

void
ConditionTestSignal (int which){
	
	l1->Acquire();
	printf ("*** Le thread %d va reveiller un thread\n", which);
	c1->Signal(l1);
	printf ("*** Le thread %d a envoye le signal\n", which);
	l1->Release();
	
}

void
ConditionTestBroadcast (int which){
	
	l1->Acquire();
	printf ("*** Le thread %d va reveiller TOUS les threads \n", which);
	c1->Broadcast(l1);
	printf ("*** Le thread %d a envoye le signal de broadcast\n", which);
	l1->Release();
	
}
//----------------------------------------------------------------------
// CommunicatorTest 
//             Effectue les tests de la classe Communicator
//----------------------------------------------------------------------

void
TestSpeak(int which){
	
	printf("**** Le thread %d essaye de parler\n",which);
	com1->Speak(123);
	printf("**** Le thread %d dit %d\n",which,123);
}

void
TestSpeak2(int which){
	
	printf("**** Le thread %d essaye de parler\n",which);
	com1->Speak(456);
	printf("**** Le thread %d a dit %d\n",which,456);
}

void
TestListen(int which){
	
	printf("**** Le thread %d ecoute\n",which);
	printf("**** Le thread %d a entendu %d\n",which,com1->Listen());
}

//----------------------------------------------------------------------
// SimpleThread
//      Loop 5 times, yielding the CPU to another ready thread 
//      each iteration.
//
//      "which" is simply a number identifying the thread, for debugging
//      purposes.
//----------------------------------------------------------------------

void
SimpleThread (int which)
{
	int num;
	int nb;
	if (which == 11)
		nb=4;
	else if (which == 21)
		nb=10;
	else
		nb=6;
	
	for (num = 0; num < nb; num++)
    {
		
        if (num == 1 && which == 11){
			Thread *tpetitfils = new Thread ("forked threadfils",1); 
			printf("### Le thread 11 (fils du thread 1) execute son fils (petit fils du thread 1). ###\n");
			tpetitfils-> Fork(SimpleThread,111);
			printf("### Le thread 11 (fils du thread 1) join son fils (petit fils du thread 1). ###\n");
			tpetitfils->Join();
			printf("### Le thread 111 (fils du thread 11) reveille son pere (fils du thread 1). ###\n");
		} 
		printf ("*** thread %d looped %d times in SimpleThread\n", which, num);
		currentThread->Yield ();
    }
}


//----------------------------------------------------------------------
// JoinTest
//     Test effectué sur la méthode Join()
//----------------------------------------------------------------------

void
JoinTest (int which){
	int num;
	int nb;
	if (which == 1)
		nb=5;
	else
		nb=8;
	
	Thread *tfils = new Thread ("forked threadfils",1); 
	for (num = 0; num < nb; num++)
    {
		
		if (num == 1 && which == 1){
			printf("### Le thread 1 execute son fils. ###\n");
			tfils->Fork (SimpleThread,11);
			printf("### Le thread 1 join son fils. ###\n");
			tfils->Join();
			printf("### Le thread 11 fils du thread 1 reveille son pere. ###\n");
		}
		if (num == 2 && which == 2){
			printf("### Le thread 2 execute son fils. ###\n");
			tfils->Fork (SimpleThread,21);
		}
		
		printf ("*** thread %d looped %d times in TestJoin\n", which, num);
		currentThread->Yield ();    
		if (num == 3 && which == 2){
			printf("### Le thread 2 join son fils. ###\n");
			tfils->Join();
			printf("### Le thread 21 fils du thread 2 reveille son pere. ###\n");
		}
    }    
	
}

//----------------------------------------------------------------------
// ThreadTest
//     Lance les différents tests
//----------------------------------------------------------------------

void
ThreadTest ()
{
	DEBUG ('t', "Entering SimpleTest\n");
	
	//----------------- Test du verrou -----------------------------
	
	printf("\n ---- Test du Verrou\n\n");
	Thread *t1 = new Thread ("forked thread1");
	Thread *t2 = new Thread ("forked thread2");
	Thread *t3 = new Thread ("forked thread3");
	l1 =  new Lock ("tested lock 1");
	l2 =  new Lock ("tested lock 2");   
	
	t1->Fork (LockTest, 1);
	t2->Fork (LockTest, 2);
	t3->Fork (LockTest, 3);
	
	PassageAutreTest();	
	printf("\n ---- Fin du test du Verrou ----\n\n");
	
	//----------------- Test des variables de conditions -----------------------------
	
	PassageAutreTest();	
	printf("\n  ---- Test des variables de condition ----\n\n");
	
	c1 = new Condition("tested condition 1");
	Thread *t5 = new Thread ("forked thread5");
	Thread *t6 = new Thread ("forked thread6");
	Thread *t7 = new Thread ("forked thread7");
	Thread *t8 = new Thread ("forked thread8");
	Thread *t9 = new Thread ("forked thread9");
	t5-> Fork (ConditionTestWait,1);
	t6-> Fork (ConditionTestWait,2);
	t7-> Fork (ConditionTestWait,3);
	t8-> Fork (ConditionTestSignal,4);
	t9-> Fork (ConditionTestBroadcast,5);
	
	PassageAutreTest();
	printf("\n ---- Fin du test des variables de condition ----\n\n");
	
	//----------------- Test de la méthode Join ---------------------
	
	PassageAutreTest();	
	printf("\n  ---- Test du Join ----\n\n");
	
	Thread *t10 = new Thread ("forked thread10");
	Thread *t11 = new Thread ("forked thread11");
	
	printf("### Le thread 1 s'execute###\n");
	t10->Fork (JoinTest,1);
	printf("### Le thread 2 s'execute###\n");
	t11->Fork (JoinTest,2);
	
	
	PassageAutreTest();
	printf("\n ---- Fin du test du join ----\n\n");
	
	
	//----------------- Test de la classe Communicator ---------------------
	
	PassageAutreTest();	
	printf("\n  ---- Test de la classe Communicator ----\n\n");
	com1 = new Communicator("tested Communicator");
	Thread *t12 = new Thread ("forked thread12");
	Thread *t13 = new Thread ("forked thread13");
	Thread *t14 = new Thread ("forked thread14");
	Thread *t15 = new Thread ("forked thread15");
	Thread *t16 = new Thread ("forked thread16");
	Thread *t17 = new Thread ("forked thread17");
	
	t12->Fork (TestListen,3);
	t13->Fork (TestSpeak2,2);
	t14->Fork (TestListen,1);
	t15->Fork (TestSpeak,4);
	t16->Fork (TestSpeak2,5);
	t17->Fork (TestListen,6);
	
	PassageAutreTest();
	printf("\n ---- Fin du test de la classe Communicator ----\n\n");
	
}
